﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Text;
using System.Threading.Tasks;
using System.Xml.Serialization;

namespace Conqueror.API
{
    [XmlRoot("api")]
    public class APIGameResponse
    {
        [XmlElement("page")]
        public string Page { get; set; }

        public int CurrentPage { get { return Int32.Parse(Page.Split(' ')[0]); } }
        public int TotalPages { get { return Int32.Parse(Page.Split(' ')[2]); } }

        [XmlElement("games")]
        public Games Content { get; set; }
    }

    public class Games
    {
        [XmlElement("game")]
        public List<Game> GameList { get; set; }
    }

    public enum GameState
    {
        None,
        [XmlEnum("W")]
        Waiting,
        [XmlEnum("A")]
        Active,
        [XmlEnum("F")]
        Finished
    }

    public enum GameType
    {
        Unknown,
        [XmlEnum("S")]
        Standard,
        [XmlEnum("C")]
        Terminator,
        [XmlEnum("A")]
        Assassin,
        [XmlEnum("D")]
        Doubles,
        [XmlEnum("T")]
        Triples,
        [XmlEnum("Q")]
        Quadruples,
        [XmlEnum("P")]
        Polymorphic
    }

    public enum InitialTroops
    {
        Unknown,
        [XmlEnum("E")]
        Automatic,
        [XmlEnum("M")]
        Manual
    }

    public enum PlayOrder
    {
        Unknown,
        [XmlEnum("S")]
        Sequential,
        [XmlEnum("F")]
        Freestyle
    }

    public enum Fortifications
    {
        Unknown,
        [XmlEnum("C")]
        Chained,
        [XmlEnum("O")]
        Adjacent,
        [XmlEnum("M")]
        Unlimited,
        [XmlEnum("P")]
        Parachute,
        [XmlEnum("N")]
        None
    }

    public enum Query
    {
        [XmlEnum("Y")]
        Yes,
        [XmlEnum("N")]
        No
    }

    public enum Spoils
    {
        Unknown,
        [XmlEnum("1")]
        NoSpoils = 1,
        [XmlEnum("2")]
        Escalating,
        [XmlEnum("3")]
        FlatRate,
        [XmlEnum("4")]
        Nuclear,
        [XmlEnum("5")]
        Zombie
    }

    public class Game
    {
        private const string mode = "gamelist";

        private static async Task<List<Game>> PerformGameRequest(Uri uri)
        {
            HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create(uri);
            if (request.Headers == null)
                request.Headers = new WebHeaderCollection();
            request.Headers[HttpRequestHeader.IfModifiedSince] = DateTime.UtcNow.ToString();
            WebResponse response = await request.GetResponseAsync();

            //Error checks
            if (response.ContentType != "application/xml")
                throw new FormatException("The response is not XML");

            XmlSerializer serializer = new XmlSerializer(typeof(APIGameResponse));
            try
            {
                return await Task.Run(() =>
                {
                    var result = (APIGameResponse)serializer.Deserialize(response.GetResponseStream());
                    return result.Content.GameList;
                });
            }
            catch (Exception e) { throw e; }
        }

        public static async Task<List<Game>> GetUserGames(string p1un, GameState state = GameState.Active)
        {
            return await PerformGameRequest(GenerateURI(p1un, state));
        }

        public static async Task<List<Game>> GetGames(string gn, GameState gs, string np, 
            GameType ty, string mp, InitialTroops it, PlayOrder po, Spoils bc, Fortifications ft, bool? wf, bool? tw)
        {
            return await PerformGameRequest(GetGamesUri(gn, gs, np, ty, mp, it, po, bc, ft, wf, tw));
        }

        private static Uri GetGamesUri(string gn, GameState gs, string np, 
            GameType ty, string mp, InitialTroops it, PlayOrder po, Spoils bc, Fortifications ft, bool? wf, bool? tw)
        {
            string uri = GenerateURI("", gs).OriginalString;
            if (!string.IsNullOrEmpty(gn))
                uri += "&gn=" + gn;
            //This is currently broken in the CC api
            //if (!string.IsNullOrEmpty(np))
            //    uri += "&np=" + np;
            if (ty != GameType.Unknown)
            {
                uri += "&ty=";
                switch(ty)
                {
                    default: uri += "S"; break;
                    case GameType.Standard: uri += "S"; break;
                    case GameType.Terminator: uri += "C"; break;
                    case GameType.Assassin: uri += "A"; break;
                    case GameType.Polymorphic: uri += "P"; break;
                    case GameType.Doubles: uri += "D"; break;
                    case GameType.Triples: uri += "T"; break;
                    case GameType.Quadruples: uri += "Q"; break;
                }
            }
            if (!string.IsNullOrEmpty(mp))
                uri += "&mp=" + mp;
            if (it != InitialTroops.Unknown)
            {
                uri += "&it=";
                switch(it)
                {
                    default: uri += "E"; break;
                    case InitialTroops.Automatic: uri += "E"; break;
                    case InitialTroops.Manual: uri += "M"; break;
                }
            }
            if (po != PlayOrder.Unknown)
            {
                uri += "&po=";
                switch(po)
                {
                    default: uri += "S"; break;
                    case PlayOrder.Sequential: uri += "S"; break;
                    case PlayOrder.Freestyle: uri += "F"; break;
                }
            }
            if (bc != Spoils.Unknown)
            {
                uri += "&bc=";
                switch(bc)
                {
                    default: uri += "2"; break;
                    case Spoils.NoSpoils: uri += "1"; break;
                    case Spoils.Escalating: uri += "2"; break;
                    case Spoils.FlatRate: uri += "3"; break;
                    case Spoils.Nuclear: uri += "4"; break;
                    case Spoils.Zombie: uri += "5"; break;
                }
            }
            if (ft != Fortifications.Unknown)
            {
                uri += "&ft=";
                switch(ft)
                {
                    default: uri += "C"; break;
                    case Fortifications.Chained: uri += "C"; break;
                    case Fortifications.Adjacent: uri += "O"; break;
                    case Fortifications.Unlimited: uri += "M"; break;
                    case Fortifications.None: uri += "N"; break;
                    case Fortifications.Parachute: uri += "P"; break;
                }
            }
            if (wf.HasValue)
            {
                uri += "&wf=" + (wf.Value ? "Y" : "N");
            }
            if (tw.HasValue)
            {
                uri += "&tw=" + (tw.Value ? "Y" : "N");
            }
            return new Uri(uri);
        }

        private static Uri GenerateURI(string p1un, GameState state = GameState.None)
        {
            string gs = "";

            switch(state)
            {
                case GameState.Active: gs = "&gs=A"; break;
                case GameState.Finished: gs = "&gs=F"; break;
                case GameState.Waiting: gs = "&gs=W"; break;
                default:
                case GameState.None: gs = ""; break;
            }
            
            if (string.IsNullOrWhiteSpace(p1un))
                return new Uri(string.Format("{0}?mode={1}&names=Y&chat=Y&events=Y{2}", API.APIAddress, mode, gs));
            else return new Uri(string.Format("{0}?mode={1}&p1un={2}&names=Y&chat=Y&events=Y{3}", API.APIAddress, mode, p1un, gs));
        }

        [XmlElement("game_number")]
        public string GameNumber { get; set; }
        [XmlElement("game_state")]
        public GameState State { get; set; }
        [XmlElement("private")]
        public Query Private { get; set; }
        [XmlElement("speed_game")]
        public Query SpeedGame { get; set; }
        [XmlElement("map")]
        public string Map { get; set; }
        [XmlElement("game_type")]
        public GameType Type { get; set; }
        [XmlElement("initial_troops")]
        public InitialTroops Troops { get; set; }
        [XmlElement("play_order")]
        public PlayOrder Order { get; set; }
        [XmlElement("bonus_cards")]
        public Spoils BonusCards { get; set; }
        [XmlElement("fortifications")]
        public Fortifications Fortification { get; set; }
        [XmlElement("war_fog")]
        public Query WarFog { get; set; }
        [XmlElement("trench_warfare")]
        public Query TrenchWarfare { get; set; }
        [XmlElement("round_limit")]
        public int RoundLimit { get; set; }
        [XmlElement("round")]
        public int Round { get; set; }
        [XmlElement("poly_slots")]
        public int PolySlots { get; set; }
        [XmlElement("time_remaining")]
        public string TimeRemaining { get; set; }
        [XmlElement("chat")]
        public string Chat { get; set; }
        [XmlElement("players")]
        public PlayerList Players { get; set; }
        [XmlElement("events")]
        public EventList Events { get; set; }
    }

    public class EventList
    {
        [XmlElement("event")]
        public List<Event> Events { get; set; }
    }

    public class Event
    {
        [XmlText()]
        public string EventText { get; set; }

        [XmlAttribute("timestamp")]
        public long Ticks { get; set; }
    }

    public class PlayerList
    {
        [XmlElement("player")]
        public List<GamePlayer> Players { get; set; }
    }

    public class GamePlayer
    {
        [XmlText()]
        public string Name { get; set; }

        [XmlAttribute("state")]
        public GamePlayerState State { get; set; }

        [XmlIgnore()]
        public string Meta { get; set; } //Metadata used for storage in other sources. TODO: Abstract this out.
    }

    public enum GamePlayerState
    {
        [XmlEnum()]
        Unknown = 100,
        [XmlEnum("Won")]
        Won = 1,
        [XmlEnum("Playing")]
        Playing = 2,
        [XmlEnum("Ready")]
        Ready = 3,
        [XmlEnum("Waiting")]
        Waiting = 4,
        [XmlEnum("Lost")]
        Lost = 5,
        [XmlEnum("Open")]
        Open
    }
}
